package com.pactera.asmp.server.common.websocket;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pactera.asmp.server.common.utils.SpringUtil;
import com.pactera.asmp.server.entity.TaskSearchDTO;
import com.pactera.asmp.server.pojo.WSMessage;
import com.pactera.asmp.server.service.ScreenDataService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/ws/screen/v1/{id}")
@Component
public class WebSocketServerScreen {
    public static final String SCREEN_CODE_CONNECTED_IMAGE = "900"; // 发送图片连接
    public static final String SCREEN_CODE_DISCONNECTED_IMAGE = "901"; // 发送图片断开
    public static final String SCREEN_CODE_CONNECTED_COMPUTERS_RUNTIME = "902"; // 发送上位机运行时状态连接
    public static final String SCREEN_CODE_DISCONNECTED_COMPUTER_RUNTIME = "903"; // 上位机运行时状态断开
    public static final String SCREEN_CODE_CONNECTED_DUID_RUNTIME = "904"; // 发送设备运行时状态连接
    public static final String SCREEN_CODE_DISCONNECTED_DUID_RUNTIME = "905"; // 设备运行时状态断开
    public static final String SCREEN_CODE_DATA_TASKCATEGORY = "906"; // 脚本类别分布
    public static final String SCREEN_CODE_DATA_TASKFAIL = "907"; // 失败率TOP5
    public static final String SCREEN_CODE_DATA_TASKING = "908"; // 正在执行的任务
    public static final String SCREEN_CODE_DATA_ISSUECHARTLINE = "909"; // 问题趋势
    public static final String SCREEN_CODE_DATA_ISSUECHARTBAR = "910"; // 各模块稳定性
    public static final String SCREEN_CODE_DATA_TASKEXECOMPLETE = "911"; // 任务执行完成
    public static final String SCREEN_CODE_DATA_TASKEXEFAILED = "912"; // 任务执行失败
    public static final String SCREEN_CODE_DATA_TASKEXEPROCESS = "913"; // 任务执行百分比改变

    private Logger logger = LoggerFactory.getLogger(WebSocketServerScreen.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();

    private SocketUtil socketUtil;
    private ScreenDataService screenDataService;

    public static volatile ConcurrentHashMap<String, Session> id2sessionMap = new ConcurrentHashMap<>();
    public static volatile ConcurrentHashMap<Session, String> session2idMap = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session, @PathParam("id") String userId) throws IOException {
        String sessionKey = socketUtil().formatSessionId(userId, session);
        if (StringUtils.isEmpty(sessionKey)) {
            session.close();
            return;
        }
        sessionKey = "screen:" + sessionKey;
        logger.info("Screen onOpen =================================================================================================================={}", sessionKey);
        session.setMaxIdleTimeout(15000);  // 15秒超时过期

        // 1. 本地缓存
        id2sessionMap.put(sessionKey, session);
        session2idMap.put(session, sessionKey);
        // 2. 分布式Session服务注册
        socketUtil().registerSocketServer(sessionKey);

        // 3. 心跳信息
        socketUtil().heartBeatById(sessionKey);

    }

    @OnClose
    public void onClose(Session session){
        if(session2idMap.containsKey(session)){
            // 1. 清本地缓存
            String id = session2idMap.get(session);
            logger.info("Screen onClose =================================================================================================================={}", id);
            session2idMap.remove(session);
            id2sessionMap.remove(id);
            // 2. 清除socket的绑定关联
            socketUtil().clearScreenRegister(id);
            socketUtil().offlineRedisCache(id);
        }
    }


    /**
     *
     * @param message
     * 1. ping#XXXXXX     心跳续约
     * 2. init:projectId  初始化
     * 3. deviceimage:xxx,xxxx,xxxx 关注图片的终端设备
     * 4. devicestate:xxx,xxx,xxxx 关注运行状态的终端设备
     * 5. computerstate:xxx,xxxx,xxxx 关注的上位机
     * @param session
     * @throws IOException
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        if(!session2idMap.containsKey(session) || StringUtils.isEmpty(message))return;
        String id = session2idMap.get(session);
        String userId = id.split(":")[1].split("@")[0];

        if(StringUtils.isEmpty(id)) return;
        if(!StringUtils.isEmpty(message) && message.startsWith("ping#")){
            message = "ping#"+id;
            socketUtil().heartBeatByMsg(message);
            return;
        }

        String[] arrMessage = message.split(":");

        // 绑定项目
        if(message.startsWith("init:")){
            bindScreenSocketProject(id,arrMessage[1]);
            initDataByProjectId(message);
            return;
        }

        // 绑定 deviceIds
        if(message.startsWith("deviceimage:")){
            String devices = message.split(":")[1];
            if(StringUtils.isEmpty(devices)) return;
            bindScreenSocketDevices(session, userId, devices, id,"deviceimage");
            return;
        }

        // 绑定上位机
        if(message.startsWith("computerstate:")){
            String computers = message.split(":")[1];
            if(StringUtils.isEmpty(computers)) return;
            bindScreenSocketDevices(session, userId, computers, id,"computerstate");
            return;
        }

        // 绑定车机
        if(message.startsWith("devicestate:")){
            String devices = message.split(":")[1];
            if(StringUtils.isEmpty(devices)) return;
            bindScreenSocketDevices(session, userId, devices, id,"devicestate");
            return;
        }
    }

    @OnMessage
    public void onMessage(byte[] bytes, Session session) {
    }

    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("设备连接错误，原因:{}", error.getMessage());
    }

    private void bindScreenSocketDevices(Session session, String userId, String ids, String sessionKey,String type) {
        // 4. 绑定设备（删除已绑定设备，增加新绑定设备）
        String code = null;
        if("deviceimage".equalsIgnoreCase(type)){
             socketUtil().bindScreenSocketDevices(sessionKey, ids);
            code = SCREEN_CODE_CONNECTED_IMAGE;
        }
        else if("computerstate".equalsIgnoreCase(type)){
             socketUtil().bindScreenSockeComputer(sessionKey, ids);
            code = SCREEN_CODE_CONNECTED_COMPUTERS_RUNTIME;
        }
        else if("devicestate".equalsIgnoreCase(type)){
             socketUtil().bindScreenSockeDuidState(sessionKey, ids);
            code = SCREEN_CODE_CONNECTED_DUID_RUNTIME;
        }
        else{
            logger.error("bindScreenSocketDevices ERROR TYPE================================={}",type);
            return;
        }

        // 给已连接的设备发送消息，表示需要发送图片或消息
        screenConnetMessage(session,ids,code);
        // 成功连接反馈
        socketUtil().openSuccessMessage(session, userId,sessionKey,null);
    }

    private void screenConnetMessage(Session session,String deviceIds, String code) {
        if(StringUtils.isEmpty(deviceIds)) return;
        List<String> devices = new ArrayList(Arrays.asList(deviceIds.split(",")));

        if(!CollectionUtils.isEmpty(devices)){
            WSMessage message = new WSMessage();
            message.setMsgcode(code);
            String sessionKey = session2idMap.get(session);
            message.setFromuuid(sessionKey.split("@")[0]);
            for (String device : devices){
                String _to = device;
                String _displayId = null;
                if (_to.indexOf('#') != -1) {
                    String[] strs = _to.split("#");
                    _to = strs[0];
                    _displayId = strs[1];
                }
                message.setTouuid(_to);
                message.setDisplayId(_displayId);
                try {
                    socketUtil().sendMQ(sessionKey,device, objectMapper.writeValueAsBytes(message), "json",null);
                } catch (JsonProcessingException e) {
                    logger.info(e.getMessage());
                }
            }
        }
    }

    private void bindScreenSocketProject(String sessionKey, String projectId) {
        socketUtil().bindScreenSocketProject(sessionKey, projectId);
    }

    /**
     * socket连接后，初始化数据
     * 1. 脚本类别分布统计
     * 2. 失败率TOP5
     * 3. 问题趋势
     * 4. 各模块稳定性统计
     * 5. 当前正在执行的任务
     * @param message
     */
    private void initDataByProjectId(String message) {
        try{
            String projectId = message.split(":")[1];
            if(StringUtils.isEmpty(projectId)) return;
//            screenDataService().asyncGetIssueChartBarByProjectId(Integer.valueOf(projectId));
//            screenDataService().asyncGetIssueChartLineByProjectId(Integer.valueOf(projectId));
//            screenDataService().asyncGetTaskCategoryDataByProjectId(Integer.valueOf(projectId));
//            screenDataService().asyncGetTaskFailDataByProjectId(Integer.valueOf(projectId));
            TaskSearchDTO searchDTO = new TaskSearchDTO();
            searchDTO.setProjectId(Integer.valueOf(projectId));
            screenDataService().asyncGetTaskingByProjectId(searchDTO);
        }
        catch (Exception e){
            logger.info(e.getMessage());
        }
    }

    private SocketUtil socketUtil() {
        if (this.socketUtil == null) this.socketUtil = SpringUtil.getBean(SocketUtil.class);
        return this.socketUtil;
    }

    private ScreenDataService screenDataService() {
        if (this.screenDataService == null) this.screenDataService = SpringUtil.getBean(ScreenDataService.class);
        return this.screenDataService;
    }
}

